Découvrez la puissance de l'API Temporal Duration de JavaScript. Ce guide complet explore les mathématiques des intervalles de temps, offrant des exemples et des conseils pratiques pour les développeurs du monde entier.
Maîtriser l'arithmétique des durées avec Temporal en JavaScript : Un guide complet sur les mathématiques des intervalles de temps
Dans le paysage en constante évolution du développement web, une gestion précise et fiable du temps est primordiale. Que vous calculiez des délais de projet à travers différents fuseaux horaires, gériez des renouvellements d'abonnement ou planifiez des événements à l'échelle mondiale, des mathématiques précises sur les intervalles de temps sont essentielles. Le JavaScript moderne a introduit un outil puissant à cet effet : l'API Temporal, et plus particulièrement, son objet Duration. Ce guide complet démystifiera l'arithmétique des durées avec JavaScript Temporal, en offrant une perspective globale sur ses capacités et ses applications pratiques.
La nécessité d'une gestion robuste du temps
Historiquement, l'objet Date natif de JavaScript a été une source de frustration pour les développeurs. Ses incohérences, son manque d'immuabilité et sa gestion complexe des fuseaux horaires et de l'heure d'été ont entraîné de nombreux bogues et un besoin persistant de bibliothèques externes. L'API Temporal, une norme proposée pour ECMAScript, vise à corriger ces problèmes en offrant une manière plus intuitive, cohérente et puissante de travailler avec les dates, les heures et les durées.
Pour un public mondial, les défis sont amplifiés. Imaginez :
- Un chef de projet à Berlin calculant le délai de livraison d'une expédition vers Tokyo, en tenant compte des différences de fuseaux horaires et des retards potentiels.
- Un analyste financier à New York déterminant la période exacte entre deux paiements d'intérêts effectués dans différents trimestres fiscaux à travers l'Europe.
- Une équipe marketing à Singapour planifiant le lancement d'une campagne mondiale, en s'assurant qu'elle coïncide avec les heures de grande écoute en Amérique du Nord, en Europe et en Asie.
Ces scénarios soulignent le besoin critique d'une approche standardisée et sans ambiguïté des mathématiques des intervalles de temps. L'objet Duration de l'API Temporal est conçu pour répondre directement à ce besoin.
Présentation de l'objet Temporal Duration de JavaScript
L'objet Temporal.Duration représente une quantité de temps, indépendante de tout point spécifique dans le temps. C'est une mesure du temps écoulé, comme '2 ans, 3 mois et 4 jours'. Contrairement aux approches précédentes qui confondaient souvent les durées avec des points dans le temps, Temporal.Duration se concentre uniquement sur la magnitude du temps. Cette séparation est la clé de sa puissance et de sa simplicité.
Composants clés d'une durée
Un objet Temporal.Duration peut représenter le temps dans diverses unités. Les principales unités qu'il prend en charge sont :
- Années (
years) - Mois (
months) - Semaines (
weeks) - Jours (
days) - Heures (
hours) - Minutes (
minutes) - Secondes (
seconds) - Millisecondes (
milliseconds) - Microsecondes (
microseconds) - Nanosecondes (
nanoseconds)
Un objet Duration peut être positif (représentant une progression du temps vers l'avant) ou négatif (représentant une progression vers l'arrière). Il est également important de noter que Temporal.Duration est immuable. Une fois créé, sa valeur ne peut pas être modifiée. Toute opération qui semble modifier une durée renvoie en fait un nouvel objet Duration.
Création de durées Temporal
Vous pouvez créer des objets Temporal.Duration de plusieurs manières, chacune adaptée à différents scénarios.
1. Utiliser la méthode Temporal.Duration.from()
C'est la méthode la plus polyvalente, vous permettant de construire une durée à partir de diverses entrées, y compris un objet littéral ou une chaîne de durée ISO 8601.
À partir d'un objet littéral :
Fournissez les unités que vous souhaitez inclure en tant que propriétés d'un objet.
const twoYearsThreeMonths = Temporal.Duration.from({
years: 2,
months: 3
});
console.log(twoYearsThreeMonths);
// Temporal.Duration { years: 2, months: 3, ... }
const oneDayEightHours = Temporal.Duration.from({
days: 1,
hours: 8,
minutes: 30
});
console.log(oneDayEightHours);
// Temporal.Duration { days: 1, hours: 8, minutes: 30, ... }
const negativeDuration = Temporal.Duration.from({
hours: -5,
minutes: -15
});
console.log(negativeDuration);
// Temporal.Duration { hours: -5, minutes: -15, ... }
À partir d'une chaîne de durée ISO 8601 :
La norme ISO 8601 fournit une représentation compacte en chaîne de caractères pour les durées. Le format est PnYnMnDTnHnMnS, où :
Pindique le début de la durée.Yreprésente les années.Mreprésente les mois.Dreprésente les jours.Tsépare les composants de date des composants de temps.Hreprésente les heures.Mreprésente les minutes.Sreprésente les secondes.
Notez que le 'M' après 'T' fait référence aux minutes, tandis que le 'M' avant 'T' fait référence aux mois. Les unités de temps (heures, minutes, secondes) sont facultatives et n'apparaissent que s'il y a une valeur non nulle.
const isoDuration1 = Temporal.Duration.from('P1Y2M3DT4H5M6S');
console.log(isoDuration1);
// Temporal.Duration { years: 1, months: 2, days: 3, hours: 4, minutes: 5, seconds: 6, ... }
const isoDuration2 = Temporal.Duration.from('P10DT5H'); // 10 jours, 5 heures
console.log(isoDuration2);
// Temporal.Duration { days: 10, hours: 5, ... }
const isoDuration3 = Temporal.Duration.from('P3M'); // 3 mois
console.log(isoDuration3);
// Temporal.Duration { months: 3, ... }
// Les chaînes ISO 8601 invalides lèveront une erreur.
// Temporal.Duration.from('PT10M5S'); // Ceci est valide
// Temporal.Duration.from('10M'); // Ceci n'est pas valide sans 'P'
2. Utiliser le constructeur Temporal.Duration()
Le constructeur permet une instanciation directe, mais il est généralement recommandé d'utiliser from() car il offre plus de flexibilité et une meilleure gestion des erreurs pour les entrées invalides.
const constructorDuration = new Temporal.Duration(0, 0, 0, 1, 2, 3); // années, mois, semaines, jours, heures, minutes
console.log(constructorDuration);
// Temporal.Duration { years: 0, months: 0, weeks: 0, days: 1, hours: 2, minutes: 3, ... }
// Note : Le constructeur prend des arguments dans un ordre fixe (années, mois, semaines, jours, heures, minutes, secondes, millisecondes, microsecondes, nanosecondes).
// Fournir moins d'arguments signifie que les unités suivantes sont traitées comme nulles.
const partialDuration = new Temporal.Duration(1, 6); // 1 an, 6 mois
console.log(partialDuration);
// Temporal.Duration { years: 1, months: 6, ... }
Accéder aux composants d'une durée
Une fois que vous avez un objet Temporal.Duration, vous pouvez accéder à ses composants individuels à l'aide de propriétés :
const myDuration = Temporal.Duration.from({
years: 5,
days: 10,
hours: 12,
minutes: 45
});
console.log(myDuration.years);
// 5
console.log(myDuration.days);
// 10
console.log(myDuration.hours);
// 12
console.log(myDuration.minutes);
// 45
console.log(myDuration.seconds); // Les unités non spécifiées sont à 0
// 0
Arithmétique des durées Temporal : Les opérations de base
La véritable puissance de l'objet Temporal.Duration réside dans ses opérations arithmétiques. Ces opérations vous permettent d'ajouter, de soustraire, de multiplier et de diviser des durées, offrant un contrôle précis sur les intervalles de temps.
1. Additionner des durées (add())
La méthode add() vous permet de combiner deux objets Temporal.Duration. Lors de l'addition de durées, les unités sont agrégées. Par exemple, ajouter '1 an' et '2 mois' donne une durée de '1 an, 2 mois'.
const duration1 = Temporal.Duration.from({ days: 10, hours: 5 });
const duration2 = Temporal.Duration.from({ days: 5, hours: 10 });
const totalDuration = duration1.add(duration2);
console.log(totalDuration);
// Temporal.Duration { days: 15, hours: 15, ... }
const duration3 = Temporal.Duration.from({ years: 1, months: 6 });
const duration4 = Temporal.Duration.from({ months: 8, days: 15 });
const combinedDuration = duration3.add(duration4);
console.log(combinedDuration);
// Temporal.Duration { years: 1, months: 14, days: 15, ... }
// Note : Il s'agit d'une simple agrégation. Temporal gérera les reports d'unités (par ex., 14 mois devenant 1 an et 2 mois) lors de l'interaction avec les objets PlainDate/Time.
// L'ajout d'une durée négative équivaut à une soustraction
const duration5 = Temporal.Duration.from({ hours: 3 });
const duration6 = Temporal.Duration.from({ hours: -1 });
const result = duration5.add(duration6);
console.log(result);
// Temporal.Duration { hours: 2, ... }
2. Soustraire des durées (subtract())
La méthode subtract() fonctionne de manière analogue à add() mais effectue une soustraction.
const durationA = Temporal.Duration.from({ days: 20, hours: 10 });
const durationB = Temporal.Duration.from({ days: 5, hours: 3 });
const remainingDuration = durationA.subtract(durationB);
console.log(remainingDuration);
// Temporal.Duration { days: 15, hours: 7, ... }
// Soustraire une durée qui aboutit à une valeur négative
const durationC = Temporal.Duration.from({ minutes: 30 });
const durationD = Temporal.Duration.from({ minutes: 45 });
const negativeResult = durationC.subtract(durationD);
console.log(negativeResult);
// Temporal.Duration { minutes: -15, ... }
3. Inverser une durée (negated())
La méthode negated() renvoie un nouvel objet Duration dont tous les composants sont inversés (le positif devient négatif, et le négatif devient positif).
const positiveDuration = Temporal.Duration.from({ hours: 10, minutes: 30 });
const negativeDuration = positiveDuration.negated();
console.log(negativeDuration);
// Temporal.Duration { hours: -10, minutes: -30, ... }
const alreadyNegative = Temporal.Duration.from({ days: -5 });
const nowPositive = alreadyNegative.negated();
console.log(nowPositive);
// Temporal.Duration { days: 5, ... }
4. Valeur absolue d'une durée (abs())
La méthode abs() renvoie un nouvel objet Duration dont tous les composants sont rendus non négatifs. Ceci est utile lorsque vous ne vous souciez que de la magnitude d'un intervalle de temps, quelle que soit sa direction.
const negativeDuration = Temporal.Duration.from({ hours: -8, minutes: -20 });
const absoluteDuration = negativeDuration.abs();
console.log(absoluteDuration);
// Temporal.Duration { hours: 8, minutes: 20, ... }
5. Multiplier des durées (multiply())
La méthode multiply() vous permet de mettre à l'échelle une durée par un nombre donné. C'est extrêmement utile pour des tâches comme le calcul du temps total pour des événements récurrents ou la détermination de jalons futurs basés sur un intervalle de base.
const dailyDuration = Temporal.Duration.from({ days: 1 });
const twoWeeks = dailyDuration.multiply(14);
console.log(twoWeeks);
// Temporal.Duration { days: 14, ... }
const hourlyIncrement = Temporal.Duration.from({ hours: 1 });
const workWeek = hourlyIncrement.multiply(40);
console.log(workWeek);
// Temporal.Duration { hours: 40, ... }
const projectPhase = Temporal.Duration.from({ months: 2 });
const fullProject = projectPhase.multiply(3);
console.log(fullProject);
// Temporal.Duration { months: 6, ... }
// La multiplication peut également se faire avec des nombres négatifs
const futureEvent = Temporal.Duration.from({ days: 5 }).multiply(-2);
console.log(futureEvent);
// Temporal.Duration { days: -10, ... }
6. Diviser des durées (divide())
La méthode divide() vous permet de diviser une durée par un nombre donné. Ceci est utile pour des tâches comme la détermination de la durée moyenne d'un événement ou la division d'un temps total en parties plus petites et égales.
Note importante sur la division : La division dans Duration de Temporal est conçue pour renvoyer un nombre entier pour chaque composant. Toute partie fractionnaire est généralement tronquée (arrondie à l'inférieur). Pour les scénarios nécessitant des résultats fractionnaires, vous travailleriez généralement avec des objets PlainDateTime ou Instant, puis calculeriez la durée résultante.
const totalWorkTime = Temporal.Duration.from({ hours: 40, minutes: 30 });
const timePerTask = totalWorkTime.divide(5);
console.log(timePerTask);
// Temporal.Duration { hours: 8, minutes: 1, ... } // 40,5 heures / 5 = 8,1 heures. Les 0,1 heures (6 minutes) sont tronquées.
const projectDuration = Temporal.Duration.from({ days: 90 });
const phaseDuration = projectDuration.divide(3);
console.log(phaseDuration);
// Temporal.Duration { days: 30, ... }
// Diviser par un nombre négatif
const longDuration = Temporal.Duration.from({ years: 2 }).divide(-4);
console.log(longDuration);
// Temporal.Duration { years: -0, ... } // -0,5 ans donne 0 an en raison de la troncature.
// Pour des calculs plus précis impliquant la division et les parties fractionnaires, envisagez d'utiliser des méthodes qui opèrent sur Temporal.Instant ou Temporal.PlainDateTime.
7. Arrondir des durées (round())
La méthode round() est cruciale pour normaliser les durées, en particulier lorsque vous traitez avec différentes unités ou lorsque vous devez exprimer une durée dans une unité spécifique avec une certaine précision. Elle prend une unité et un mode d'arrondi comme arguments.
Les modes d'arrondi courants incluent :
Temporal.RoundingMode.trunc: Tronque vers zéro.Temporal.RoundingMode.floor: Arrondit à l'inférieur.Temporal.RoundingMode.ceil: Arrondit au supérieur.Temporal.RoundingMode.halfExpand: Arrondit vers l'infini positif, les moitiés étant arrondies en s'éloignant de zéro.
const impreciseDuration = Temporal.Duration.from({
hours: 2,
minutes: 35,
seconds: 45
});
// Arrondir Ă la minute la plus proche, en utilisant halfExpand (arrondi standard)
const roundedToMinute = impreciseDuration.round('minute', Temporal.RoundingMode.halfExpand);
console.log(roundedToMinute);
// Temporal.Duration { hours: 2, minutes: 36, ... } // 35 minutes et 45 secondes s'arrondissent Ă 36 minutes
// Tronquer Ă l'heure la plus proche
const truncatedToHour = impreciseDuration.round('hour', Temporal.RoundingMode.trunc);
console.log(truncatedToHour);
// Temporal.Duration { hours: 2, ... } // Ignore les minutes et les secondes.
// Arrondir à l'heure supérieure
const ceiledToHour = impreciseDuration.round('hour', Temporal.RoundingMode.ceil);
console.log(ceiledToHour);
// Temporal.Duration { hours: 3, ... } // Comme il y a des minutes et des secondes, cela arrondit au supérieur.
// L'arrondi à une unité plus petite (par ex., aux secondes) peut révéler plus de précision
const preciseRounding = impreciseDuration.round('second', Temporal.RoundingMode.halfExpand);
console.log(preciseRounding);
// Temporal.Duration { hours: 2, minutes: 35, seconds: 45, ... }
8. Comparer des durées (compare())
La méthode statique Temporal.Duration.compare() est utilisée pour comparer deux objets Duration. Elle renvoie :
1si la première durée est supérieure à la seconde.-1si la première durée est inférieure à la seconde.0si les durées sont égales.
La comparaison est effectuée en convertissant les deux durées en une unité commune la plus petite (nanosecondes), puis en comparant leurs valeurs numériques. Cela garantit une comparaison précise quelles que soient les unités utilisées dans les objets de durée d'origine.
const durationX = Temporal.Duration.from({ days: 1, hours: 12 }); // 1,5 jours
const durationY = Temporal.Duration.from({ hours: 36 }); // 1,5 jours
const durationZ = Temporal.Duration.from({ days: 2 }); // 2 jours
console.log(Temporal.Duration.compare(durationX, durationY)); // 0 (égal)
console.log(Temporal.Duration.compare(durationX, durationZ)); // -1 (durationX est inférieure à durationZ)
console.log(Temporal.Duration.compare(durationZ, durationY)); // 1 (durationZ est supérieure à durationY)
// Comparaison avec des durées négatives
const negDuration1 = Temporal.Duration.from({ hours: -5 });
const negDuration2 = Temporal.Duration.from({ hours: -10 });
console.log(Temporal.Duration.compare(negDuration1, negDuration2)); // 1 (par ex., -5 est supérieur à -10)
Travailler avec les durées et les dates/heures
Bien que Temporal.Duration représente une quantité de temps, sa véritable utilité se manifeste souvent lorsqu'elle est combinée avec des points spécifiques dans le temps ou des objets date/heure comme Temporal.PlainDate, Temporal.PlainDateTime, Temporal.ZonedDateTime et Temporal.Instant. Les opérations arithmétiques sur ces objets utiliseront implicitement des calculs de durée.
Ajouter/Soustraire des durées à des dates/heures
Les méthodes comme add() et subtract() sur les objets date/heure prennent une Duration en argument. C'est là que les complexités de l'arithmétique du calendrier (comme les années bissextiles, les mois avec des jours variables) sont gérées par Temporal.
// Exemple avec Temporal.PlainDate (nécessite un polyfill ou un support natif)
// En supposant que vous ayez un polyfill Temporal ou un support natif dans votre environnement.
// Imaginons qu'aujourd'hui soit le 15 juillet 2024
const today = Temporal.PlainDate.from({ year: 2024, month: 7, day: 15 });
const durationToAdd = Temporal.Duration.from({ years: 1, months: 3, days: 15 });
const futureDate = today.add(durationToAdd);
console.log(futureDate);
// Temporal.PlainDate { year: 2025, month: 11, day: 1 }
// Exemple global : Calculer une date future en tenant compte des différentes longueurs de mois
const londonDate = Temporal.PlainDate.from({ year: 2024, month: 1, day: 31 }); // 31 janvier
const durationForNextMonth = Temporal.Duration.from({ months: 1 });
const nextMonthDate = londonDate.add(durationForNextMonth);
console.log(nextMonthDate);
// Temporal.PlainDate { year: 2024, month: 2, day: 29 } // Gère correctement l'année bissextile et la fin du mois.
const newYorkDate = Temporal.ZonedDateTime.from({
timeZone: 'America/New_York',
year: 2024,
month: 10,
day: 28,
hour: 10,
minute: 0,
second: 0
});
const travelDuration = Temporal.Duration.from({ hours: 8 }); // Un vol de 8 heures
// Note : Lors de l'ajout de durées à ZonedDateTime, il est crucial de tenir compte du fuseau horaire.
// Le résultat sera dans le même fuseau horaire sauf indication contraire.
const arrivalTimeNY = newYorkDate.add(travelDuration);
console.log(arrivalTimeNY);
// Temporal.ZonedDateTime { year: 2024, month: 10, day: 28, hour: 18, minute: 0, second: 0, ... }
// Si vous voulez calculer l'heure d'arrivée dans un fuseau horaire DIFFÉRENT, vous feriez généralement :
// 1. Ajouter la durée au ZonedDateTime de départ.
// 2. Convertir le ZonedDateTime résultant dans le fuseau horaire de destination.
const tokyoTimeZone = 'Asia/Tokyo';
const arrivalTimeTokyo = arrivalTimeNY.withTimeZone(tokyoTimeZone);
console.log(arrivalTimeTokyo);
// Temporal.ZonedDateTime { year: 2024, month: 10, day: 29, hour: 7, minute: 0, second: 0, ... } (Notez que la date et l'heure changent en raison du fuseau horaire)
Calculer la durée entre des dates/heures
Les méthodes until() et since() sur les objets date/heure renvoient un Temporal.Duration. C'est ainsi que vous mesurez le temps écoulé entre deux points.
const startDate = Temporal.PlainDate.from({ year: 2023, month: 1, day: 1 });
const endDate = Temporal.PlainDate.from({ year: 2024, month: 3, day: 15 });
const elapsedDuration = startDate.until(endDate);
console.log(elapsedDuration);
// Temporal.Duration { years: 1, months: 2, days: 14, ... }
// Exemple global : Calculer la différence de durée d'un contrat
const contractStart = Temporal.ZonedDateTime.from({
timeZone: 'UTC',
year: 2022,
month: 5,
day: 10,
hour: 9,
minute: 0
});
const contractEnd = Temporal.ZonedDateTime.from({
timeZone: 'UTC',
year: 2025,
month: 8,
day: 20,
hour: 17,
minute: 30
});
const contractLength = contractStart.until(contractEnd);
console.log(contractLength);
// Temporal.Duration { years: 3, months: 3, days: 10, hours: 8, minutes: 30, ... }
// Lors de l'utilisation de until/since avec ZonedDateTime, le résultat peut être complexe en raison des fuseaux horaires et de l'heure d'été.
// Temporal gère cela en vous donnant une durée qui pourrait ne pas 'boucler' parfaitement si vous la rajoutez sans tenir compte du fuseau horaire.
Bonnes pratiques et considérations globales
Lorsque vous travaillez avec les durées Temporal, en particulier dans un contexte mondial, gardez ces points à l'esprit :
-
L'immuabilité est la clé : Traitez toujours les objets
Durationcomme immuables. Toute opération renvoie un nouvel objet, évitant ainsi les effets de bord involontaires. -
Comprendre l'agrégation d'unités par rapport à l'arithmétique du calendrier : L'arithmétique de
Durationelle-même effectue une simple agrégation d'unités. Lorsque vous combinez uneDurationavec un objet date/heure, les méthodes de Temporal (commeadd()surPlainDate) effectuent une arithmétique tenant compte du calendrier, qui est plus sophistiquée et prend en compte les longueurs de mois variables, les années bissextiles, etc. -
Les fuseaux horaires sont extrêmement importants : Pour toute application traitant avec des utilisateurs ou des événements dans différentes régions, l'utilisation de
Temporal.ZonedDateTimeest essentielle. L'objetDurationlui-même est agnostique au fuseau horaire, mais son application avecZonedDateTimenécessite une gestion attentive pour représenter correctement le temps écoulé entre différentes zones. - ISO 8601 est votre ami : Tirez parti des chaînes ISO 8601 pour les durées chaque fois que possible. Elles sont standardisées, sans ambiguïté, et faciles à analyser et à générer, ce qui les rend idéales pour l'échange de données entre systèmes et pour la cohérence internationale.
-
Choisissez un arrondi approprié : La méthode
round()est puissante mais nécessite de comprendre vos besoins en matière d'arrondi. Pour les calculs financiers, des règles d'arrondi spécifiques peuvent s'appliquer. Pour l'affichage général de l'heure,halfExpandest généralement approprié. - Pensez à l'expérience utilisateur : Lors de l'affichage des durées aux utilisateurs, envisagez de localiser la sortie. Bien que Temporal fournisse la durée brute, présenter 'P1Y2M' comme '1 an et 2 mois' ou même '14 mois' peut être plus convivial selon le contexte et la locale.
- Adoptez le standard : L'API Temporal est conçue pour devenir un standard. À mesure qu'elle gagne en adoption et en support par les navigateurs, s'appuyer sur elle simplifiera votre code et le rendra plus maintenable et pérenne.
Conclusion
L'API Temporal de JavaScript, avec son objet Duration, représente un bond en avant significatif dans la gestion des calculs basés sur le temps. En fournissant un cadre robuste, immuable et mathématiquement solide pour l'arithmétique des durées, elle permet aux développeurs de créer des applications plus fiables et précises. Que vous gériez des projets internationaux, développiez des outils de planification mondiaux ou ayez simplement besoin de calculs d'intervalles de temps précis, la maîtrise de l'arithmétique des durées Temporal sera une compétence inestimable pour tout développeur JavaScript moderne.
Alors que le monde devient de plus en plus interconnecté, la capacité à gérer avec précision et intuition les intervalles de temps à travers différentes régions et contextes n'est plus un luxe mais une nécessité. L'objet Temporal.Duration est votre clé pour débloquer cette capacité, ouvrant la voie à des applications plus sophistiquées et conscientes du contexte mondial.